package com.cskaoyan.javase.recursion._0introduction;

/**
 * 递归的引入
 * 我们已经学习过方法，使用过方法了。方法体中是可以调用方法的，那么如果在方法体中调用方法自身呢？
 * 特别的，在一个方法当中再次调用这个方法，就像故事里提到同样的故事一样，
 * **我们把方法在运行时调用自身的情况，称之为递归，又叫做递归调用。**
 *
 * 递归的使用：
 *      1.要想使用递归，必须在方法体中，自己调用自己，这句自身调用自身的语句叫做"递归体"
 *      2.有递归体还不行，要想递归能够终止，避免栈溢出错误，就需要"递归的出口"，即什么时候不调用自身方法了
 * 递归的两要素：
 *      1.递归体
 *      2.递归的出口
 * 思考：
 *      递归体有了，递归的出口有了，那么递归就是安全的吗？
 *      不是，仍然需要考虑递归的次数（深度），如果递归的深度超过了栈内存空间仍未到达出口，仍然会栈溢出错误
 * 综上，递归好用，但是递归很危险，稍有不慎就会导致栈溢出错误，所以要谨慎使用它。
 *
 * @since 10:43
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        System.out.println(sum(20000));
        /*
            分析一下这个程序：
            sum(10) = 10 + sum(9);
            sum(9) = 9 + sum(8);
            sum(8) = 8 + sum(7);
            ...
            这个过程显然没有终止的时候，请猜想程序的执行结果是什么？
            1.死循环
            2.报错
            3.电脑炸了（炸不了）

            方法的调用会占用栈内存空间，像上面那样，方法只进栈，不出栈，栈空间会不够用，
            从而导致StackOverflowError栈溢出错误，Error是比Exception更严重的错误，程序一旦出现Error必定终止执行
            报错的原因是方法只进不出，没有出口，所以递归需要一个出口，来结束方法的调用

            假如num=0是出口
            那么
            sum(0) = 0;
         */
    }

    // 求前n(n>0)个数的和
    // Method 'sum()' recurses infinitely, and can only end by throwing an exception
    public static int sum(int num) {
        // 当num = 0的时候，就没必要继续减了，这时就是递归的出口
        if (num == 0) {
            return 0;
        }
        return num + sum(num - 1);
    }
}
